package com.maychat.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.maychat.config.AppConfig;
import com.maychat.config.Consts;
import com.maychat.enums.*;
import com.maychat.exception.BusinessException;
import com.maychat.mapper.CustomUserIdInfoMapper;
import com.maychat.mapper.UserContactMapper;
import com.maychat.mapper.UserInfoMapper;
import com.maychat.pojo.*;
import com.maychat.pojo.extended.Message;
import com.maychat.service.ChatSessionUserService;
import com.maychat.service.UserContactService;
import com.maychat.service.UserInfoService;
import com.maychat.utils.StringTools;
import com.maychat.websocket.MessageHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class UserInfoServiceImpl implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private RedisServiceImpl redisService;
    @Autowired
    private CustomUserIdInfoMapper customUserIdInfoMapper;
    @Autowired
    private UserContactMapper userContactMapper;
    @Autowired
    private UserContactService userContactService;
    @Autowired
    private ChatSessionUserService chatSessionUserService;
    @Autowired
    private MessageHandler messageHandler;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void register(String email, String password, String nickname) throws BusinessException {
        UserInfo userInfo = new UserInfo();
        userInfo.setEmail(email);
        List<UserInfo> list = userInfoMapper.selectUserInfos(userInfo);
        if (!list.isEmpty()) throw new BusinessException("邮箱已经存在");
        CustomUserIdInfo custom = customUserIdInfoMapper.getByEmailStatus0(email);
        String UID;
        if (custom == null) UID = StringTools.generateRandomUserId();
        else {
            UID = custom.getTargetUserId();
            custom.setStatus(1);
            customUserIdInfoMapper.updateById(custom);
        }
        userInfo.setUserId(UID);
        userInfo.setPassword(StringTools.cryptoMD5(password));
        userInfo.setNickName(nickname);
        userInfo.setCreateTime(LocalDateTime.now());
        userInfo.setStatus(UserStatus.ENABLED.ordinal());
        userInfo.setLastOfflineTime(System.currentTimeMillis());
        userInfo.setJoinType(JoinType.CHECK_FIRST.ordinal());
        userInfoMapper.insertUserInfo(userInfo);
        userContactService.addRobot(UID);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, Object> login(String email, String password) throws BusinessException {
        UserInfo userInfo = new UserInfo();
        userInfo.setEmail(email);
        userInfo = userInfoMapper.selectUserInfoWithPwd(userInfo);
        if (userInfo == null) throw new BusinessException("邮箱或密码错误");
        if (!StringTools.cryptoMD5(password).equals(userInfo.getPassword()))
            throw new BusinessException("邮箱或密码错误");
        if (userInfo.getStatus() == UserStatus.DISABLED.ordinal())
            throw new BusinessException("账号已禁用！请联系管理员");
        UserInfoToken token = createUserInfoToken(userInfo);
        Long last = redisService.getUserConnHeartBeat(token.getUserId());
        if (last != null) throw new BusinessException("账号已登录，请先下线！如不是您的登录，请联系管理员");
        userInfo.setLastLoginTime(LocalDateTime.now());
        userInfoMapper.updateUserInfoById(userInfo);
        String encoded = StringTools.cryptoMD5(token.getUserId() + RandomStringUtils.random(20));
        token.setToken(encoded);
        redisService.saveUserToken(token);
        Map<String, Object> mp = new HashMap<>();
        mp.put("token", token.getToken());
        userInfo.setPassword(null);
        log.info(String.valueOf(userInfo));
        mp.put("userInfo", userInfo);
        mp.put("admin", token.isAdmin());
        // 查询联系人、群组
        UserContact query = new UserContact();
        query.setUserId(userInfo.getUserId());
        query.setStatus(UserContactStatus.FRIEND.ordinal());
        List<String> ids = userContactMapper.selectUserContacts(query).stream().map(uc -> uc.getContactId()).toList();
        redisService.clearUserContacts(userInfo.getUserId());
        if (!ids.isEmpty())
            redisService.saveUserContacts(userInfo.getUserId(), ids);
        return mp;
    }

    @Override
    public List<UserInfo> selectUserInfosByUserContact(UserContact userContact) {
        return userInfoMapper.selectUserInfosByUserContact(userContact);
    }

    @Override
    public UserInfo selectUserInfoById(String userId) {
        return userInfoMapper.selectUserInfoById(userId);
    }

    @Override
    public void updateUserInfoById(UserInfo userInfo, MultipartFile avatarSmall, MultipartFile avatarFull) throws IOException {
        if (avatarFull != null && avatarSmall != null) {
            String fileFolder = appConfig.getProjectFolder() + Consts.FOLDER_FILE_AVATAR;
            File file = new File(fileFolder);
            if (!file.exists()) file.mkdirs();
            String avatarBasePath = fileFolder + "/" + userInfo.getUserId();
            avatarSmall.transferTo(new File(avatarBasePath + Consts.IMAGE_SMALL_SUFFIX));
            avatarFull.transferTo(new File(avatarBasePath + Consts.IMAGE_SUFFIX));
        }
        UserInfo origin = userInfoMapper.selectUserInfoById(userInfo.getUserId());
        if (!userInfo.allFieldsNullExceptId()) {
            log.info("{}", userInfo);
            userInfoMapper.updateUserInfoById(userInfo);
        }
        String newNameUpdated = null;
        if (!origin.getNickName().equals(userInfo.getNickName()))
            newNameUpdated = userInfo.getNickName();
        if (newNameUpdated == null) return;
        // 更新会话的昵称？
        chatSessionUserService.updateRedundant(userInfo.getUserId(), newNameUpdated);
        // 更新redis？
        UserInfoToken tokenObj = redisService.getUserTokenByUserId(userInfo.getUserId());
        if (tokenObj == null) return;
        tokenObj.setNickName(newNameUpdated);
        redisService.saveUserToken(tokenObj);
    }

    @Override
    public void updatePassword(String userId, String oldPassword, String newPassword) throws BusinessException {
        UserInfo query = new UserInfo();
        query.setUserId(userId);
        query = userInfoMapper.selectUserInfoWithPwd(query);
        if (query == null) throw new BusinessException();
        oldPassword = StringTools.cryptoMD5(oldPassword);
        if (!oldPassword.equals(query.getPassword()))
            throw new BusinessException("原密码不正确");
        newPassword = StringTools.cryptoMD5(newPassword);
        UserInfo newUserInfo = new UserInfo();
        newUserInfo.setUserId(userId);
        newUserInfo.setPassword(newPassword);
        userInfoMapper.updateUserInfoById(newUserInfo);
    }

    @Override
    public RowsWithTotal<UserInfo> adminSelectUsers(UserInfo userInfo, Integer page, Integer pageSize) {
        PageHelper.startPage(page, pageSize);
        Page<UserInfo> p = (Page<UserInfo>) userInfoMapper.selectUserInfos(userInfo);
        return new RowsWithTotal<>(p.getTotal(), p.getResult());
    }

    @Override
    public void updateUserStatusById(String userId, Integer status) throws BusinessException {
        UserStatus userStatus = UserStatus.getByOrdinal(status);
        if (userStatus == null) throw new BusinessException();
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId(userId);
        userInfo.setStatus(status);
        userInfoMapper.updateUserInfoById(userInfo);
    }

    @Override
    public void kick(String userId) {
        // 发送ws
        Message<Object> msg = new Message<>();
        msg.setContactId(userId);
        msg.setContactType(ContactType.USER.ordinal());
        msg.setMessageType(MessageType.FORCED_OFFLINE.ordinal());
        messageHandler.sendMsg(msg);
    }

    private UserInfoToken createUserInfoToken(UserInfo userInfo) {
        UserInfoToken token = new UserInfoToken();
        token.setUserId(userInfo.getUserId());
        token.setNickName(userInfo.getNickName());
        boolean contains = false;
        String emails;
        if (!StringUtils.isBlank((emails = appConfig.getAdminsEmail())))
            contains = ArrayUtils.contains(emails.split(","), userInfo.getEmail());
        token.setAdmin(contains);
        return token;
    }
}
